/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package it.polimi.registration.business.security.boundary;

import it.polimi.registration.business.security.entity.BadWeather;
import it.polimi.registration.business.security.entity.Event;
import it.polimi.registration.business.security.entity.Forecast;
import it.polimi.registration.business.security.entity.Invitation;
import it.polimi.registration.business.security.entity.Notification;
import it.polimi.registration.business.security.entity.User;
import it.polimi.registration.business.weather.PropositionFactory;
import it.polimi.registration.business.weather.WeatherConstants;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.Principal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 *
 * @author miglie
 */
@Stateless
public class EventManager {

    @PersistenceContext
    private EntityManager em;
    
    @EJB
    private NotificationManager nm;
    
    @EJB
    private CalendarManager cm;
    
    @EJB
    private UserManager um;
    
    @Inject
    private Principal principal;    

    @Inject
    private Logger logger;

    public void save(Event event) {
        Invitation i;
        
        //Actual Time
        Calendar c = new GregorianCalendar();
        Forecast f = null;
        long timespan = (event.getDate().getTime().getTime() - c.getTime().getTime())/1000;
        if(timespan >= 0 && timespan < WeatherConstants.SIXTEEN_DAYS) {
        	f = new Forecast(event, event.getDate(), event.getLocation());
        	//em.persist(f);
        	event.setForecast(f);
    		
        	try {
				f.update();
			} catch (Exception e) {
            	StringWriter errors = new StringWriter();
            	e.printStackTrace(new PrintWriter(errors));
				logger.log(Level.SEVERE, errors.toString());
			}
        }
        
        if(timespan < WeatherConstants.THREE_DAYS) {
			BadWeather bw;
			Calendar proposition = getProposition(event);
			for(User u : event.getInvitedUsers()) {
				bw = new BadWeather(event, u, proposition);
	        	nm.save(bw);
	        	um.merge(u);
	        	cm.merge(u.getCalendar());
			}
			
			bw = new BadWeather(event, event.getOwner(), proposition);
        	nm.save(bw);
        	um.merge(event.getOwner());
        	cm.merge(event.getOwner().getCalendar());
		}
        
        event.getOwner().getCalendar().addEvent(event);
        um.merge(event.getOwner());
        cm.merge(event.getOwner().getCalendar());
        
        em.persist(event);
        if(f != null) em.merge(f);
        
        for(User u : event.getInvitedUsers()) {
        	u.getCalendar().addEvent(event);
        	i = new Invitation(event, u);
        	nm.save(i);
        	um.merge(u);
        	cm.merge(u.getCalendar());
        }
    }

	public Event getEvent(int id) {
		return (Event) em.createQuery("SELECT t FROM event t where t.id = :value1")
                .setParameter("value1", id).getSingleResult();
	}

	public void delete(Event event) {
		//if(event.getHasForecast())
			//em.remove(event.getForecast());
		
		List<Object> toRemove = null;
		
		for(User u : event.getInvitedUsers()) {
			toRemove = u.remEvent(event);
			
			for(Object o : toRemove) {
				em.remove(o);
			}
			em.merge(u.getCalendar());
			em.merge(u);
		}
		
		toRemove = event.getOwner().remEvent(event);
		
		for(Object o : toRemove) {
			em.remove(o);
		}
		em.merge(event.getOwner().getCalendar());
		em.merge(event.getOwner());
		
		//em.remove(event);
	}

	public void update(Event event) {
		Invitation i;
		
		//if(event.getHasForecast())
			//em.remove(event.getForecast());
		
        //Actual Time
        Calendar c = new GregorianCalendar();
        Forecast f = null;
        long timespan = (event.getDate().getTime().getTime() - c.getTime().getTime())/1000;
        if(timespan >= 0 && timespan < WeatherConstants.SIXTEEN_DAYS) {
        	f = new Forecast(event, event.getDate(), event.getLocation());
        	em.persist(f);
        	event.setForecast(f);
    		
        	try {
				f.update();
			} catch (Exception e) {
            	StringWriter errors = new StringWriter();
            	e.printStackTrace(new PrintWriter(errors));
				logger.log(Level.SEVERE, errors.toString());
			}
        }
        
        em.merge(event);
        if(f != null) em.merge(f);
        
        List<Notification> ln;
        boolean exists;
        
        for(User u : um.getAllUsers()) {
			logger.log(Level.SEVERE, u.getEmailAddress());
        	if(!event.getInvitedUsers().contains(u)) {
    			logger.log(Level.SEVERE, u.getEmailAddress());
        		List<Object> toRemove = u.remEvent(event);
    			logger.log(Level.SEVERE, toRemove.size() + "");
        		
        		for(Object o : toRemove) {
        			em.remove(o);
        		}
        		
        		em.merge(u.getCalendar());
    			em.merge(u);
        	}
        }
        
        for(User u : event.getInvitedUsers()) {
        	ln = u.getNotifications();
        	exists = false;
        	
        	for(Notification n : ln) {
        		if(n instanceof Invitation) {
        			i = (Invitation) n;
        			if(i.getEvent().equals(event)) {
        				exists = true;
        				break;
        			}
        		}
        	}
        	
        	if(!exists) {
	        	u.getCalendar().addEvent(event);
	        	i = new Invitation(event, u);
	        	nm.save(i);
	        	um.merge(u);
	        	cm.merge(u.getCalendar());
        	}
        }
	}
	
	private Calendar getProposition(Event e) {
		Calendar cal=GregorianCalendar.getInstance();
		long ts;
		try {
			ts = (new PropositionFactory(e)).getNextSunnyDay();			
			if(ts == 0)
				cal = e.getDate();
			else
				cal.setTime(new Timestamp(ts*1000));
		} catch (Exception e1) {
			cal = e.getDate();
		}
		return cal;
	}

	public void merge(Event event) {
		em.merge(event);
	}    
}
